/**
* JS library. 
*
* @version 0.0.1 Beta
* @author IvanS
*/

var OOJ = OOJ || {};
OOJ.lang = (function () {
	
	
	/**
 	* @method extend
 	* @param {Object} subClass
 	* @param {Object} superClass
 	*/
	var extend = function(subClass, superClass){
		if (!subClass || !superClass) 
			throw new Error("!");
		// create a help function
		var F = function(){
		};
		F.prototype = superClass.prototype;
		subClass.prototype = new F();
		subClass.prototype.constructor = subClass;
		subClass.superclass = superClass.prototype;
		
		if (superClass.prototype.constructor == Object.prototype.constructor) {
			superClass.prototype.constructor = superClass;
		}
	};
	
	var Event = function () {}
	
	Event.prototype = {
		
		_event: null,
		
		getStandardEvent: function () {},
		
		cancelBubble: function () {},
	
	}
	
	Event.isStandard = function (type) {
		
	}
	
	var Body = {
		
	}
	
	
	/**
	 * @class Component
	 */
	var Component = function () {}
	
	Component.prototype = {
		
		/**
		 * @property _eventHandlers
		 * @private
		 * @type Array
		 */
		_eventHandlers: [],
		
		/**
		 * @method addEventHandler
		 */
		addEventHandler: function (type, handler) {
			this._eventHandlers[type] = handler;
		},
		
		/**
		 * @method removeEventHandler
		 */
		removeEventHandler: function () {
			delete this._eventHandlers[type];
		},
		
		/**
		 * @method fireEvent
		 */
		fireEvent: function (type) {
			if (this._eventHandlers[type]) {
				this._eventHandlers[type]();
			}
		}
	}
	
	
	/**
	 * 
	 */
	var Element = function () {}
	
	extend(Element, Component);
	
	Element.prototype = {
		
		addEventHandler: function (type, handler) {
			this._eventHandlers[type] = handler;
		},
		
		hasClass: function () {
			
		},
		
		addClass: function () {
			
		},
		
		removeClass: function () {
			
		},
		
		replaceClass: function () {
			
		}

	}
	
	
	
	
	
	return {
		extend: extend,
		Interface: Interface
	}
	
})();




















var OOJ = {
	
};

(function () {
	OOJ.lang = {};
	
	
	
	/**
	 * Element class
     * @constructor 
     * @param {HTMLElement} - standard html element.
     */
    var Element = function(element){
        if (!element) {
            throw new Error("UndefinedParameterException");
        }
        
        var _element = element;
        
        var _eventHandlers = {};
        
        
        /**
         * Retrieves an event handler function relatively the given event type
         * @param {String} type - event name
         * @return {Function} - event handler fucntion
         */
        var _fireEvent = function (type) {
        	var events = OOJ.events;
        	var _Event;
        	if (type == events.CLICK || type == events.MOUSEDOWN
        	       || type == events.MOUSEMOVE || type == events.MOUSEUP
        	       || type == events.MOUSEOVER || type == events.MOUSEDOWN) {
        		_Event = OOJ.lang.MouseEvent;
        	} else {
        		_Event = OOJ.lang.Event;
        	}
        	return function (e) {
        		var _ev = e || window.event;
        		_eventHandlers[type](new _Event(_ev));
        	}
        }
        
        
        /**
         * Returns the source html element
         * @return {HTMLElement}
         */
        this.getElement = function () {
            return _element;
        }
        
        
        /**
         * Assgns an event handler for the event of given type 
         * @param {String} type - an event type name
         * @param {Function} handler - a function with one argument that 
         *      will be called when the event fires
         */
        this.addEventHandler = function (type, handler) {
        	_eventHandlers[type] = handler;
        	_element["on" + type] = _fireEvent(type);
        }
        
        
        /**
         * Removes an event handler for the event of given type
         * @param {String} type - an event type name 
         */
        this.removeEventHandler = function (type) {
        	delete _eventHandlers[type];
        	_element["on" + type] = null;
        }
        
    };
	
	
	/**
     * Retrieves the ID attribute value of the element
     * @return {String}    
     */
    Element.prototype.getId = function () {
    	return this.getElement().id;
    }
    
	
    /**
     * Sets the ID attribute value of the element
     * @param {String} - a new element ID  
     */
    Element.prototype.getId = function (id) {
        return this.getElement().id = id;
    }
    
    
    /**
     * Retrieves whether the element has the css class
     * @param {String} className - a css class name
     * @return {Boolean} 
     */
    Element.prototype.hasClass = function (className) {
        return new RegExp("(^|\\s)" + className + "(\\s|$)")
            .test(this.getElement().className);
    }
    
    
    /**
     * Adds the css class to the element
     * @param {String} className - a name of the class to be added
     */
    Element.prototype.addClass = function (className) {
        if (this.hasClass(className)) {
        	return;
        }
        this.getElement().className += " " + className;
    }
    
    
    /**
     * Removes the css class from the element
     * @param {String} className - a name of the class to be removed
     */
    Element.prototype.removeClass = function (className) {
        var element = this.getElement();
        var classes = element.className.split(/\s+/);
        for (var i = 0; i < classes.length; i++) {
            if (classes[i] == className) {
                delete classes[i];
            }
        }
        element.className = classes.join(" ");
    }
    
    
    /**
     * Retrieves the element tag name lowercased
     * @return {String}
     */
    Element.prototype.getTagName = function () {
        return this.getElement().tagName.toLowerCase();
    }
    
    
    /**
     * Retrieves the childs of the element as a element set
     * @return {OOJ.lang.ElementSet}
     */
    Element.prototype.getChilds = function () {
    	return new ElementSet(this.getElement().childs);
    }
    
    
    /**
     * Inserts a child node to the element
     * @param {OOJ.lang.Element | HTMLElement} element - an instance
     *      of the OOJ.lang.Element or a standart html element  
     */
    Element.prototype.appendChild = function (element) {
    	var _el = this.getElement();
    	if (element instanceof OOJ.lang.Element) {
    		_el.appendChild(element.getElement());
    	} else {
    		_el.appendChild(element);
    	}
    }
    
    
    /**
     * Removes a child node from the element
     * @param {OOJ.lang.Element | HTMLElement} element - an instance
     *      of the OOJ.lang.Element or a standart html element  
     */
    Element.prototype.removeChild = function (element) {
        var _el = this.getElement();
        if (element instanceof OOJ.lang.Element) {
            _el.removeChild(element.getElement());
        } else {
            _el.removeChild(element);
        }
    }
    
    
    /**
     * Retrieves the parent node of the element
     * @return {OOJ.lang.Element} 
     */
    Element.prototype.getParent = function () {
        return new Element(this.getElement().parentNode);
    }
    
    
    /**
     * Retrieves the childs by a css class name
     * @param {String} className - a css class name 
     */
    Element.prototype.getChildsByClass = function (className) {
        var element = this.getElement();
        var retnode = [];
        var myclass = new RegExp('\\b' + className + '\\b');
        var childs = element.getElementsByTagName('*');
        for (var i = 0; i < childs.length; i++) {
            var classes = childs[i].className;
            if (myclass.test(classes)) {
                retnode.push(childs[i]);
            }
        }
        return new ElementSet(retnode);
    }
    
    
    /**
     * Retrieves an element by value its ID attribute
     * @param {String} id - an ID attribute value 
     */
    Element.getById = function (id) {
    	var el = OOJ.cache[id];
    	if (el) {
    		return el;
    	} 
        return new Element(document.getElementById(id));
    }
	
	
	OOJ.lang.Element = Element;
	
	
	
	
}) ();



OOJ.ex  = {
    UndefinedParameter: function (param) {
        return new Error("UndefinedParameterException: Parameter '" + param + "' is undefined");
    }
};

OOJ.cache = {};

OOJ.lang.extend = function (subClass, superClass) {
    if (!subClass || !superClass) throw new Error("!");
    // create a help function
    var F = function () {};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
    subClass.uber = superClass.prototype;
    
    if(superClass.prototype.constructor == Object.prototype.constructor) {
        superClass.prototype.constructor = superClass;
    }
}


OOJ.Browser = (function () {
	var userAgent = navigator.userAgent.toLowerCase();
    return {
        isSafari: function () {
            return /webkit/.test(userAgent);
        },
        isOpera: function () {
            return /opera/.test(userAgent);
        },
        isIE: function () {
            return /msie/.test(userAgent) && !/opera/.test(userAgent);
        },
        isFF: function () {
            return /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent);
        }
    }
})();

(function () {
    OOJ.events = {
    	ABORT:         'abort',
    	CLICK:         'click',
    	BLUR:          'blur',
    	CHANGE:        'change',
    	CLICK:         'click',
    	ERROR:         'error',
    	FOCUS:         'focus',
    	KEYDOWN:       'keydown',
    	KEYPRESS:      'keypress',
    	KEYUP:         'keyup',
    	LOAD:          'load',
    	MOUSEDOWN:     'mousedown',
    	MOUSEMOVE:     'mousemove',
    	MOUSEOUT:      'mouseout',
    	MOUSEOVER:     'mouseover',
    	MOUSEUP:       'mouseup',
    	RESET:         'reset',
    	RESIZE:        'resize',
    	SCROLL:        'scroll',
    	SELECT:        'select',
    	SUBMIT:        'submit',
    	UNLOAD:        'unload'
    }
    
    var extend = OOJ.lang.extend;
    
    
    /**
     * OOJ.lang.Event is the super class for all event classes.
     * It encapsulates the event object and adds methods for 
     * setting or retrieving event properties
     * @constructor
     * @param {Event} ev - the event object 
     */
    var Event = function (ev) {
    	var _ev = ev;
    	
    	/**
    	 * Returns the event object
    	 * @return {Event}
    	 */
    	this.getEvent = function () {
    		return _ev;
    	}
    }
    
    
    Event.prototype = {
    	
    	/**
         * Sets whether the current event should 
         * bubble up the hierarchy of event handlers. 
         * @param {Boolean} b 
         */
        bubbling:  function (b) {
            this.getEvent().cancelBubble = !b;
        },
        
        
        /**
         * Retrieves the event name from the event object.
         * @return {String} 
         */
        getType:  function () {
            return this.getEvent().type;
        },
        
        
        /**
         * Retrieves the element that fired the event. 
         * @return {OOJ.lang.Element}
         */
        getSrcElement: function () {
           var _src = (this.getEvent().target) || (this.getEvent().srcElement);
           return new OOJ.lang.Element(_src);   
        },
        
        
        /**
         * Retrieves whether the CTRL key is pressed.
         * @return {Boolean}
         */
        isCtrlPressed: function () {
            return this.getEvent().ctrlKey;
        },
        
        
        /**
         * Retrieves whether the SHIFT key is pressed.
         * @return {Boolean}
         */
        isShiftPressed: function () {
            return this.getEvent().shiftKey;
        },
        
        
        /**
         * Retrieves whether the ALT key is pressed.
         * @return {Boolean}
         */
        isAltPressed: function () {
            return this.getEvent().altKey;
        }
        
    }


    
    /**
     * OOJ.lang.MouseEvent class extends the OOJ.lang.Event class 
     * and adds methods for retrieving properties related to the cursor position
     * or the mouse buttons state   
     * @extends OOJ.lang.Event
     * @constructor
     * @param {Event} ev - the event object
     */
    var MouseEvent = function (e) {
    	MouseEvent.uber.constructor.call(this, e)
    }
    extend(MouseEvent, Event);
    
    
    /**
     * Retrieves the x-coordinate of the mouse pointer's position
     * relative to the user's screen.
     * @return {Integer} 
     */
    MouseEvent.prototype.getScreenX = function () {
		return this.getEvent().screenX;
	};
	
	
	/**
     * Retrieves the y-coordinate of the mouse pointer's position
     * relative to the user's screen.
     * @return {Integer} 
     */
    MouseEvent.prototype.getScreenY = function () {
        return this.getEvent().screenY;
    };
    
    
    /**
     * Retrieves the x-coordinate of the mouse pointer's position 
     * relative to the client area of the window, excluding window 
     * decorations and scroll bars.
     * @return {Integer} 
     */
    MouseEvent.prototype.getClientX = function () {
        return this.getEvent().clientX;
    };
    
    
    /**
     * Retrieves the y-coordinate of the mouse pointer's position 
     * relative to the client area of the window, excluding window 
     * decorations and scroll bars.
     */
    MouseEvent.prototype.getClientY = function () {
        return this.getEvent().clientY;
    };
    
    
    /**
     * Retrieves the x-coordinate, in pixels, of the mouse pointer's position
     * relative to a relatively positioned parent element.
     * @return {Integer}
     */
    MouseEvent.prototype.getX = function () {
        return (this.getEvent().x) || (this.getEvent().layerX);
    };
    
    
    /**
     * Retrieves the y-coordinate, in pixels, of the mouse pointer's position
     * relative to a relatively positioned parent element.
     * @return {Integer}
     */
    MouseEvent.prototype.getY = function () {
        return (this.getEvent().y) || (this.getEvent().layerY);
    };
    
    
    /**
     * Retrieves the mouse button pressed by the user
     * @return {Integer} - one of the following values:
     *      0 - Default. No button is pressed.
     *      1 - Left button is pressed.
     *      2 - Right button is pressed.
     *      3 - Left and right buttons are both pressed.
     *      4 - Middle button is pressed.
     *      5 - Left and middle buttons both are pressed.
     *      6 - Right and middle buttons are both pressed.
     *      7 - All three buttons are pressed.    
     */
    MouseEvent.prototype.getButton =  function () {
    	return this.getEvent().button;
    };
    
    
    
    /**
     * OOJ.lang.KayEvent class extends the OOJ.lang.Event class 
     * and adds methods for retrieving properties related to the 
     * key pressed   
     * @extends OOJ.lang.Event
     * @constructor
     * @param {Event} ev - the event object
     */
    var KeyEvent = function () {
    	this.uber.constructor.call(this, e)
    }
    extend(KeyEvent, Event);
    
    KeyEvent.prototype = {
    	
    	/**
    	 * Retrieves the Unicode key code associated with the key 
    	 * that caused the event.
    	 * @return {Integer}  
    	 */
    	getKeyCode: function () {
    		this.getEvent().keyCode;
    	}
    	
    }
    
    OOJ.lang.Event = Event;
    OOJ.lang.MouseEvent = MouseEvent;
    
    
    /**
     * @constructor 
     * @param {HTMLElement} - an standart html element.
     */
    var Element = function (element) {
        if (!element) {
            throw new Error("UndefinedParameterException");
        }
        
        var _element = element;
        
        var _eventHandlers = {};
        
        
        /**
         * Retrieves an event handler function relatively the given event type
         * @param {String} type - event name
         * @return {Function} - event handler fucntion
         */
        var _fireEvent = function (type) {
        	var events = OOJ.events;
        	var _Event;
        	if (type == events.CLICK || type == events.MOUSEDOWN
        	       || type == events.MOUSEMOVE || type == events.MOUSEUP
        	       || type == events.MOUSEOVER || type == events.MOUSEDOWN) {
        		_Event = OOJ.lang.MouseEvent;
        	} else {
        		_Event = OOJ.lang.Event;
        	}
        	return function (e) {
        		var _ev = e || window.event;
        		_eventHandlers[type](new _Event(_ev));
        	}
        }
        
        
        /**
         * Returns the source html element
         * @return {HTMLElement}
         */
        this.getElement = function () {
            return _element;
        }
        
        
        /**
         * Assgns an event handler for the event of given type 
         * @param {String} type - an event type name
         * @param {Function} handler - a function with one argument that 
         *      will be called when the event fires
         */
        this.addEventHandler = function (type, handler) {
        	_eventHandlers[type] = handler;
        	_element["on" + type] = _fireEvent(type);
        }
        
        
        /**
         * Removes an event handler for the event of given type
         * @param {String} type - an event type name 
         */
        this.removeEventHandler = function (type) {
        	delete _eventHandlers[type];
        	_element["on" + type] = null;
        }
        
    };
    
    
    /**
     * Retrieves the ID attribute value of the element
     * @return {String}    
     */
    Element.prototype.getId = function () {
    	return this.getElement().id;
    }
    
    
    /**
     * Sets the ID attribute value of the element
     * @param {String} - a new element ID  
     */
    Element.prototype.getId = function (id) {
        return this.getElement().id = id;
    }
    
    
    /**
     * Retrieves whether the element has the css class
     * @param {String} className - a css class name
     * @return {Boolean} 
     */
    Element.prototype.hasClass = function (className) {
        return new RegExp("(^|\\s)" + className + "(\\s|$)")
            .test(this.getElement().className);
    }
    
    
    /**
     * Adds the css class to the element
     * @param {String} className - a name of the class to be added
     */
    Element.prototype.addClass = function (className) {
        if (this.hasClass(className)) {
        	return;
        }
        this.getElement().className += " " + className;
    }
    
    
    /**
     * Removes the css class from the element
     * @param {String} className - a name of the class to be removed
     */
    Element.prototype.removeClass = function (className) {
        var element = this.getElement();
        var classes = element.className.split(/\s+/);
        for (var i = 0; i < classes.length; i++) {
            if (classes[i] == className) {
                delete classes[i];
            }
        }
        element.className = classes.join(" ");
    }
    
    
    /**
     * Retrieves the element tag name lowercased
     * @return {String}
     */
    Element.prototype.getTagName = function () {
        return this.getElement().tagName.toLowerCase();
    }
    
    
    /**
     * Retrieves the childs of the element as a element set
     * @return {OOJ.lang.ElementSet}
     */
    Element.prototype.getChilds = function () {
    	return new ElementSet(this.getElement().childs);
    }
    
    
    /**
     * Inserts a child node to the element
     * @param {OOJ.lang.Element | HTMLElement} element - an instance
     *      of the OOJ.lang.Element or a standart html element  
     */
    Element.prototype.appendChild = function (element) {
    	var _el = this.getElement();
    	if (element instanceof OOJ.lang.Element) {
    		_el.appendChild(element.getElement());
    	} else {
    		_el.appendChild(element);
    	}
    }
    
    
    /**
     * Removes a child node from the element
     * @param {OOJ.lang.Element | HTMLElement} element - an instance
     *      of the OOJ.lang.Element or a standart html element  
     */
    Element.prototype.removeChild = function (element) {
        var _el = this.getElement();
        if (element instanceof OOJ.lang.Element) {
            _el.removeChild(element.getElement());
        } else {
            _el.removeChild(element);
        }
    }
    
    
    /**
     * Retrieves the parent node of the element
     * @return {OOJ.lang.Element} 
     */
    Element.prototype.getParent = function () {
        return new Element(this.getElement().parentNode);
    }
    
    
    /**
     * Retrieves the childs by a css class name
     * @param {String} className - a css class name 
     */
    Element.prototype.getChildsByClass = function (className) {
        var element = this.getElement();
        var retnode = [];
        var myclass = new RegExp('\\b' + className + '\\b');
        var childs = element.getElementsByTagName('*');
        for (var i = 0; i < childs.length; i++) {
            var classes = childs[i].className;
            if (myclass.test(classes)) {
                retnode.push(childs[i]);
            }
        }
        return new ElementSet(retnode);
    }
    
    
    /**
     * Retrieves an element by value its ID attribute
     * @param {String} id - an ID attribute value 
     */
    Element.getById = function (id) {
    	var el = OOJ.cache[id];
    	if (el) {
    		return el;
    	} 
        return new Element(document.getElementById(id));
    }
    
    OOJ.lang.Element = Element;
    $ = OOJ.lang;
    $e = OOJ.lang.Element;
    
    
    var ElementSet = function (elements) {
        var _elements = elements;
        
        this.get = function(index) {
            if (index < _elements.length) {
                return new Element(_elements[index]);
            }
            return null;
        }
        
        this.removeClass = function (className) {
            for (var i = 0; i < _elements.length; i++) {
                var el = this.get(i);
                el.removeClass(className);
            }
        }
        this.addClass = function (className) {
            for (var i = 0; i < _elements.length; i++) {
                var el = this.get(i);
                el.addClass(className);
            }
        }
        this.getSize = function () {
            return _elements.length;
        }
    }
    
    var AJAX = function (xhr) {
    	/*
    	 * Variable 'request' contains an instance of XMLHttpRequest object 
    	 * Variable 'callback' obtains a function that is called when a valid server response is got.
    	 */
        var request = xhr;
        var callback = null;
        
        // This private method is called when a request ready state changes.
        // It calls a method 'callback' and passes an instance of XMLHttpRequest object. 
        // Then 'callback' variable is set to null; 
        var readystatechange = function () {
            if (request.readyState == 4) {
            	if (request.status == 200) {
	                if (callback) {
	                    callback(request);
	                    callback = null;
	                }
            	} else if (request.status == 404) {
            		alert("404");
            	}
            	
            } 
        }
        //request.onreadystatechange = readystatechange;
        /**
         * Sends AJAX request to the server.
         *
         * @param {Object} config - a configuration object that encapsulates parameters 
         *      required for a proper requset sending.
         *      A config instance can be got by calling a static method createConfig(). 
         *      Example:
         *      <pre>
         *          var config = OOJ.AJAX.createConfig();
         *      </pre>
         * 
         *      The config object contains the following fields:
         *      <ul>
         *      <li><b>method</b> - a method using for request sending. It can be eihter 'GET' or 'POST'.</li>
         *      <li><b>url</b> - URL of a request handler. It's a mandatory parameter.</li>
         *      <li><b>params</b> - An associative array that contains key-value request parameter pairs.
         * It is required only for 'GET' request types. </li>
         *      <li><b>asynch</b> - Specifies if the request should be asynchronous or not.</li>
         *      <li><b>username</b> - Not required. It's needed if a server requires the 
         * credentials for authentication.</li>
         *      <li><b>password</b> - Not required. See the description above.</li>
         *      <li><b>content</b> - Required only for 'POST' requests.</li>
         *      <li><b>responseHandler</b> - A function that is called when a server response is got. 
         * It applies one argument that contains an instance of XMLHttpRequest object.</li>
         *      </ul>
         *      Example:
         *      <pre>
         *      // create an instance of the object OOJ.AJAX
         *      var ajax  = OOJ.AJAX.createInstance();
         *      // create an empty (default) instance of the config object 
         *      var config = OOJ.AJAX.createConfig();
         *      // set URL
         *      config.url = "/SomeApp/SomeServlet"
         *      config.asynch = true;
         *      // Specify parameters
         *      config.params = {"someParameter": "somevalue", "someparmeter2", "somevalue2"};
         *      // specify a response handler
         *      config.responseHandler = function (xhttpr) {
         *          var responseText = xhttpr.responseText;
         *          alert(responseText);
         *      }
         *      ajax.sendRequest(config);
         *      </pre>
         */
        this.sendRequest = function (config)  {
        	// if a field config.method is not specified a value 'GET' is assigned 
            var method = config.method || "GET";
            
            // create parameter string
            var paramStr = "";
            for (var k in config.params) {
                paramStr += k + "=" + config.params[k] + "&";
            }
            
            var url;
            // if the parameter string is not empty, concat it with the url. 
            if (paramStr.length) {
                url = config.url + "?" + paramStr;
            } else {
            	url = config.url;
            }
            if (config.responseHandler) {
                callback = config.responseHandler;
            }
            
            request.open(method, url, config.asynch, config.username, config.password);
            // assign the readystate handler
            // It's important to assign the readystate handler after opening the request in order 
            // the previous request data is resetted
            request.onreadystatechange = readystatechange;
            if (method == "GET") {
                request.send(null);
            } else {
                request.send(config.content);
            }
        }
    }
    
    /**
     * Creates an instance of XMLHttpRequest object
     * @return {XMLHttpRequest} - an instance of XMLHttpRequest object
     */
    AJAX.createInstance = function () {
        var request = null;
        try {
            request = new XMLHttpRequest();
        } catch (trymicrosoft) {
            try {
                request = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (othermicrosoft) {
                try {
                    request = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (failed) {
                    request = null;
                }
            }
        }
        return new AJAX(request);
    }
    
    /**
     * Creates an empty instance of the config object
     * @return {Object} - an instance of the config object
     */
    AJAX.createConfig = function () {
        return {
            method: null,
            url: null,
            params: [],
            asynch : true,
            username: null,
            password: null,
            content: null,
            responseHandler: null
        }
    }
    OOJ.AJAX = AJAX;
    
}) ();

